using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

//控制整个棋盘的渲染的脚本
public class FieldView : MonoBehaviour
{
  public const float RENDER_STEP = 0.15f;
  public const string HOLDER_KEY = "refresh_field_view";
  private class RenderHolder : IResolveEngine<FieldModel>.IHolder
  {
    private FieldView m_fieldView;
    private bool m_isHolding;
    private float holdTimer;

    public RenderHolder(FieldView fieldView) {
      m_fieldView = fieldView;
    }

    public void Hold(IResolveEngine<FieldModel>.ResolveContext context) {
      holdTimer = RENDER_STEP;
      m_isHolding = true;
      m_fieldView.Render(context.Model);
    }

    public void Tick() {
      if (m_isHolding)
      {
        holdTimer -= Time.deltaTime;
        if (holdTimer < 0)
        {
          m_isHolding = false;
        }
      }
    }

    public bool IsHolding() {
      return m_isHolding;
    }

    public void Release() {
      m_isHolding = false;
    }
  }


  [SerializeField]
  private FieldBallView _ballViewPrefab;
  [SerializeField]
  private Grid _ballViewGrid;

  private Dictionary<int, BallResources> m_resourcesDict;

  private Dictionary<int, FieldBallView> m_aliveBallViews = new Dictionary<int, FieldBallView>();

  private Vector2Int m_cachedGridSize;

  private RenderHolder m_renderHolder;

  public void SetResourcesDict(Dictionary<int, BallResources> dict) {
    m_resourcesDict = dict;
  }

  public void Render(FieldModel fieldModel) {
    if (fieldModel == null)
    {
      return;
    }
    m_cachedGridSize = fieldModel.param.size;
    UpdateAliveBallViews(fieldModel);
    CheckNotRenderedBall(fieldModel);
  }

  public IResolveEngine<FieldModel>.IHolder GetRenderHolder() {
    if (m_renderHolder == null)
    {
      m_renderHolder = new RenderHolder(this);
    }
    return m_renderHolder;
  }

  /*根据view的关联ptr指向的球来更新view
   *因为可能存在一种情况，ballModel已经被移除棋盘，但是view不能立刻消失，
   *还需要访问ballModel来播动效之类的
   */
  private void UpdateAliveBallViews(FieldModel fieldModel) {
    // copy a list to avoid iterator modify
    var list = new List<FieldBallView>(m_aliveBallViews.Values);
    for (int i = 0; i < list.Count; i++)
    {
      var ballView = list[i];
      var ptr = ballView.relatedPtr;
      var ballModel = fieldModel.gridModel.GetBallModel(ptr);
      var viewModel = ConvertToBallViewModel(ballModel);
      ballView.Render(viewModel);
    }
  }


  private void CheckNotRenderedBall(FieldModel fieldModel) {
    foreach (var ballModel in fieldModel.gridModel.AllBallModels())
    {
      var ptr = ballModel.ptr;
      if (ptr == 0) continue;
      if (!m_aliveBallViews.ContainsKey(ptr))
      {
        var ballView = CreateBallView();
        var viewModel = ConvertToBallViewModel(ballModel);
        var topCell = new Vector2Int(ballModel.position.x, m_cachedGridSize.y + 1);
        var topPosition = GridPositionToWorld(topCell);

        ballView.ForceSetPosition(topPosition);
        ballView.Render(viewModel);
        m_aliveBallViews[ptr] = ballView;
      }
    }
  }

  private FieldBallView CreateBallView() {
    var ballView = Instantiate(_ballViewPrefab, transform);
    var handlers = new FieldBallView.Handlers();
    handlers.removeFromUpperView = RemoveBallView;
    handlers.loadResources = LoadBallResources;
    handlers.gridPositionToWorld = GridPositionToWorld;
    ballView.handlers = handlers;
    return ballView;
  }

  private void RemoveBallView(int ptr) {
    m_aliveBallViews.Remove(ptr);
  }

  private BallResources LoadBallResources(int id) {
    return m_resourcesDict.SafeGet(id, null);
  }

  private Vector2 GridPositionToWorld(Vector2Int grid) {
    return _ballViewGrid.CellToWorld((grid - m_cachedGridSize / 2).ToV3I());
  }

  private FieldBallView.Model ConvertToBallViewModel(FieldModel.BallModel ballModel) {
    var viewModel = new FieldBallView.Model();
    viewModel.relatedPtr = ballModel.ptr;
    viewModel.info = ballModel.info;
    viewModel.position = ballModel.position;
    viewModel.isAlive = ballModel.isAlive;
    viewModel.gridSize = m_cachedGridSize;
    return viewModel;
  }

  private void Update() {
    m_renderHolder?.Tick();
  }
}
